1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 
12 /**
13 *   Asset representation of a texture
14 */
15 module hip.assets.texture;
16 import hip.error.handler;
17 import hip.assets.texture;
18 import hip.math.rect;
19 import hip.assets.image;
20 public import hip.util.data_structures:Array2D;
21 public import hip.api.renderer.texture;
22 public import hip.api.data.image;
23 public import hip.api.graphics.color;
24 public import hip.api.renderer.core: HipResourceUsage;
25 
26 class HipTexture : HipAsset
27 {
28     IImage img;
29     // int width,height;
30     private __gshared HipTexture pixelTexture;
31 
32     bool hasSuccessfullyLoaded(){return img.getWidth > 0;}
33     public static HipTexture getPixelTexture()
34     {
35         if(pixelTexture is null)
36         {
37             pixelTexture = new HipTexture(HipResourceUsage.Immutable);
38             pixelTexture.img = cast(IImage)Image.getPixelImage();
39             pixelTexture.textureImpl.load(pixelTexture.img);
40         }
41         return pixelTexture;
42     }
43 
44     /**
45     *   Make it available for implementors
46     */
47     IHipTexture textureImpl;
48     /**
49     *   Initializes with the current renderer type
50     */
51     protected this(HipResourceUsage usage)
52     {
53         import hip.api.renderer.core;
54         super("HipTexture");
55         _typeID = assetTypeID!HipTexture;
56         textureImpl = HipRenderer.getTextureImplementation(usage);
57     }
58 
59     this(const IImage image, HipResourceUsage usage = HipResourceUsage.Immutable)
60     {
61         this(usage);
62         this.img = cast()image;
63         if(image !is null)
64             textureImpl.load(image);
65     }
66 
67     Rect getBounds(){return Rect(0,0,getWidth,getHeight);}
68 
69     import hip.util.string;
70         SmallString toHipString()
71     {
72         return SmallString("TEX[", getWidth, "x",getHeight,"] ", img.getSizeBytes, " bytes");
73     }
74     override void onFinishLoading(){}
75     override void onDispose(){}
76 
77     override bool isReady() const {return textureImpl !is null;}
78 
79     alias textureImpl this;
80 }
81 
82 
83 class HipTextureRegion : HipAsset, IHipTextureRegion
84 {
85     static immutable float[8] defaultVertices = [0,0, 1,0, 1,1, 0,1];
86     static immutable Vector2[4] defaultVerticesV = [Vector2(0,0), Vector2(1,0), Vector2(1,1), Vector2(0,1)];
87     IHipTexture texture;
88     public float u1, v1, u2, v2;
89     protected float[8] vertices;
90     protected float[8] verticesTransformed;
91     private bool flippedX, flippedY;
92     int regionWidth, regionHeight;
93 
94     bool hasSuccessfullyLoaded(){return texture && texture.hasSuccessfullyLoaded;}
95 
96     protected this()
97     {
98         super("TextureRegion");
99         _typeID = assetTypeID!HipTextureRegion;
100     }
101 
102     this(IHipTexture texture, float u1 = 0, float v1 = 0, float u2 = 1, float v2 = 1)
103     {
104         this();
105         this.texture = texture;
106         setRegion(u1,v1,u2,v2);
107     }
108     this(IHipTexture texture, uint u1, uint v1, uint u2, uint v2)
109     {
110         this();
111         this.texture = texture;
112         setRegion(texture.getWidth, texture.getHeight, u1,  v1, u2, v2);
113     }
114 
115     void setTexture(IHipTexture texture){this.texture = texture;}
116     const(IHipTexture) getTexture() const {return cast(const)texture;}
117     IHipTexture getTexture() {return texture;}
118     int getWidth() const {return regionWidth;}
119     int getHeight() const {return regionHeight;}
120     TextureCoordinatesQuad getRegion() const
121     {
122         return TextureCoordinatesQuad(u1, v1, u2, v2);
123     }
124 
125     /**
126     * By passing the width and height values, you'll be able to crop useless frames
127     * Default spritesheet method that makes a spritesheet from the entire texture
128     */
129     public static Array2D!IHipTextureRegion cropSpritesheet(
130         IHipTexture t,
131         uint frameWidth, uint frameHeight,
132         uint width = 0, uint height = 0,
133         uint offsetX = 0, uint offsetY = 0,
134         uint offsetXPerFrame = 0, uint offsetYPerFrame = 0)
135     {
136         if(width == 0) width = t.getWidth;
137         if(height == 0) height = t.getHeight;
138 
139         uint lengthW = width/(frameWidth+offsetXPerFrame);
140         uint lengthH = height/(frameHeight+offsetYPerFrame);
141 
142         Array2D!IHipTextureRegion ret = Array2D!IHipTextureRegion(lengthH, lengthW);
143 
144         for(int i = 0, fh = 0; fh < height; i++, fh+= frameHeight+offsetXPerFrame)
145             for(int j = 0, fw = 0; fw < width; j++, fw+= frameWidth+offsetYPerFrame)
146                 ret[i,j] = new HipTextureRegion(t, offsetX+fw , offsetY+fh, offsetX+fw+frameWidth, offsetY+fh+frameHeight);
147 
148         return ret;
149     }
150     public static Array2D!IHipTextureRegion cropSpritesheetRowsAndColumns(IHipTexture t, uint rows, uint columns)
151     {
152         uint frameWidth = t.getWidth() / columns;
153         uint frameHeight = t.getHeight() / rows;
154         return cropSpritesheet(t,frameWidth,frameHeight, t.getWidth, t.getHeight, 0, 0, 0, 0);
155     }
156 
157 
158     alias setRegion = IHipTextureRegion.setRegion;
159     /**
160     *   Defines a region for the texture in the following order:
161     *   Top-left
162     *   Top-Right
163     *   Bot-Right
164     *   Bot-Left
165     */
166     public void setRegion(float u1, float v1, float u2, float v2)
167     {
168         this.u1 = u1;
169         this.u2 = u2;
170         this.v1 = v1;
171         this.v2 = v2;
172         //Check for round
173         float regWidth =  (u2 - u1) * texture.getWidth;
174         float regHeight = (v2 - v1) * texture.getHeight;
175         regionWidth =  cast(uint)(regWidth + 0.5) > cast(uint)regWidth ? cast(uint)(regWidth+0.5) : cast(uint)regWidth;
176         regionHeight = cast(uint)(regHeight + 0.5) > cast(uint)regHeight ? cast(uint)(regHeight+0.5) : cast(uint)regHeight;
177 
178         //Top left
179         vertices[0] = u1;
180         vertices[1] = v1;
181 
182         //Top right
183         vertices[2] = u2;
184         vertices[3] = v1;
185 
186         //Bot right
187         vertices[4] = u2;
188         vertices[5] = v2;
189 
190         //Bot left
191         vertices[6] = u1;
192         vertices[7] = v2;
193 
194         if(flippedX)
195         {
196             flippedX = false;
197             setFlippedX(true);
198         }
199         if(flippedY)
200         {
201             flippedY = false;
202             setFlippedY(true);
203         }
204     }
205 
206     HipTextureRegion clone()
207     {
208         return new HipTextureRegion(texture, u1, v1, u2, v2);
209     }
210     void setFlippedX(bool flip)
211     {
212         if(flip != flippedX)
213         {
214             flippedX = flip;
215             vertices[0] = flip ? u2 : u1;
216             vertices[2] = flip ? u1 : u2;
217             vertices[4] = flip ? u1 : u2;
218             vertices[6] = flip ? u2 : u1;
219         }
220     }
221     void setFlippedY(bool flip)
222     {
223         if(flip != flippedY)
224         {
225             flippedY = flip;
226             vertices[1] = flip ? v2 : v1;
227             vertices[3] = flip ? v2 : v1;
228             vertices[5] = flip ? v1 : v2;
229             vertices[7] = flip ? v1 : v2;
230         }
231     }
232     bool isFlippedX(){return flippedX;}
233     bool isFlippedY(){return flippedY;}
234 
235     ref float[8] getVertices()
236     {
237         return vertices;
238     }
239     override void onFinishLoading(){}
240     override void onDispose(){}
241     override bool isReady() const {return texture !is null;}
242 
243 }